﻿using System;
using System.Runtime.InteropServices;

namespace VoIP.NET.IAX
{

    /* ====================================================================
    * Copyright (c) 2006 Andre Luis Azevedo (az.andrel@yahoo.com.br)
    * All rights reserved.
    *                       
    * Redistribution and use in source and binary forms, with or without
    * modification, are permitted provided that the following conditions
    * are met:
    *
    * 1. Redistributions of source code must retain the above copyright
    *    notice, this list of conditions and the following disclaimer. 
    *
    * 2. Redistributions in binary form must reproduce the above copyright
    *    notice, this list of conditions and the following disclaimer in
    *    the documentation and/or other materials provided with the
    *    distribution.
    * 
    * 3. The name "ALAZ Library" must not be used to endorse or promote 
    *    products derived from this software without prior written permission.
    *
    * 4. Products derived from this software may not be called "ALAZ" nor 
    *    may "ALAZ Library" appear in their names without prior written 
    *    permission of the author.
    * 
    * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
    * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
    * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR
    * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
    * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
    * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
    * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
    * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
    * OF THE POSSIBILITY OF SUCH DAMAGE. 
    */

    #region Exceptions

    public class IAXMethodException : Exception
    {
        public IAXMethodException(string message) : base(message) { }
    }

    #endregion

    #region Delegates

    public delegate void OnRegistrationDelegate();
    public delegate void OnAudioLevelDelegate(int input, int output);
    public delegate void OnLineStateDelegate(int lineNumber);
    public delegate void OnTextStateDelegate(int lineNumber, TextType textType, string text);

    #endregion

    #region Enums

    public enum DTMFSounds
    {
        dsZero,
        dsOne,
        dsTwo,
        dsThree,
        dsFour,
        dsFive,
        dsSix,
        dsSeven,
        dsEight,
        dsNine,
        dsAsterisk,
        dsSharp
    }

    public enum RegistrationStatus
    {
        rsNotRequired = 0,
        rsCompleted = 1,
        rsRejected = 2,
        rsTimeOut = 3
    }

    public enum CallStatus
    {
        csRinging = 0,
        csBusy = 1,
        csComplete = 2,
        csCalling = 3,
        csNone = 4,
        csTransfer = 5
    }

    public enum CallDirection
    {
        cdIncoming = 0,
        cdOutgoing = 1
    }

    public enum AudioType
    {
        atAUDIO_INTERNAL = 0,
        atAUDIO_INTERNAL_PA = 1,
        atAUDIO_INTERNAL_FILE = 2,
        atAUDIO_INTERNAL_ALSA = 3,
        atAUDIO_EXTERNAL = 99
    }

    [Flags]
    public enum AudioCapability
    {

        acIAXC_AD_INPUT = (1 << 0),
        acIAXC_AD_OUTPUT = (1 << 1),
        axIAXC_AD_RING = (1 << 2),
        acIAXC_AD_INPUT_DEFAULT = (1 << 3),
        acIAXC_AD_OUTPUT_DEFAULT = (1 << 4),
        acIAXC_AD_RING_DEFAULT = (1 << 5)
    }

    [Flags]
    public enum AudioFormat
    {
        afIAXC_FORMAT_G723_1 = (1 << 0),     /* G.723.1 compression */
        afIAXC_FORMAT_GSM = (1 << 1),        /* GSM compression */
        afIAXC_FORMAT_ULAW = (1 << 2),       /* Raw mu-law data (G.711) */
        afIAXC_FORMAT_ALAW = (1 << 3),       /* Raw A-law data (G.711) */
        afIAXC_FORMAT_G726 = (1 << 4),      /* ADPCM, 32kbps  */
        afIAXC_FORMAT_ADPCM = (1 << 5),      /* ADPCM IMA */
        afIAXC_FORMAT_SLINEAR = (1 << 6),    /* Raw 16-bit Signed Linear (8000 Hz) PCM */
        afIAXC_FORMAT_LPC10 = (1 << 7),      /* LPC10, 180 samples/frame */
        afIAXC_FORMAT_G729A = (1 << 8),      /* G.729a Audio */
        afIAXC_FORMAT_SPEEX = (1 << 9),      /* Speex Audio */
        afIAXC_FORMAT_ILBC = (1 << 10),      /* iLBC Audio */
        afIAXC_FORMAT_MAX_AUDIO = (1 << 15), /* Maximum audio format */
        afIAXC_FORMAT_JPEG = (1 << 16),      /* JPEG Images */
        afIAXC_FORMAT_PNG = (1 << 17),       /* PNG Images */
        afIAXC_FORMAT_H261 = (1 << 18),      /* H.261 Video */
        afIAXC_FORMAT_H263 = (1 << 19),      /* H.263 Video */
        afIAXC_FORMAT_H263_PLUS = (1 << 20), /* H.263+ Video */
        afIAXC_FORMAT_MPEG4 = (1 << 21),     /* MPEG4 Video */
        afIAXC_FORMAT_H264 = (1 << 23),      /* H264 Video */
        afIAXC_FORMAT_THEORA = (1 << 24),    /* Theora Video */
    }

    public enum EventType
    {
        etIAXC_EVENT_TEXT = 1,
        etIAXC_EVENT_LEVELS = 2,
        etIAXC_EVENT_STATE = 3,
        etIAXC_EVENT_NETSTAT = 4,
        etIAXC_EVENT_URL = 5,	            /* URL push via IAX(2) */
        etIAXC_EVENT_VIDEO = 6,	            /* video data (pointer) */
        etIAXC_EVENT_REGISTRATION = 7
    }

    public enum RegistrationEvent
    {
        reIAXC_REGISTRATION_REPLY_ACK = 18,         /* IAX_EVENT_REGACC  */
        reIAXC_REGISTRATION_REPLY_REJ = 30,         /* IAX_EVENT_REGREJ  */
        reIAXC_REGISTRATION_REPLY_TIMEOUT = 6      /* IAX_EVENT_TIMEOUT */
    }

    [Flags]
    public enum CallState
    {
        csIAXC_CALL_STATE_FREE = 0,
        csIAXC_CALL_STATE_ACTIVE = (1 << 1),
        csIAXC_CALL_STATE_OUTGOING = (1 << 2),
        csIAXC_CALL_STATE_RINGING = (1 << 3),
        csIAXC_CALL_STATE_COMPLETE = (1 << 4),
        csIAXC_CALL_STATE_SELECTED = (1 << 5),
        csIAXC_CALL_STATE_BUSY = (1 << 6),
        csIAXC_CALL_STATE_TRANSFER = (1 << 7)
    }

    public enum TextType
    {
        ttIAXC_TEXT_TYPE_STATUS = 1,
        ttIAXC_TEXT_TYPE_NOTICE = 2,
        ttIAXC_TEXT_TYPE_ERROR = 3,
        ttIAXC_TEXT_TYPE_FATALERROR = 4,  /* FATAL ERROR: User Agent should probably display error, then die. */
        ttIAXC_TEXT_TYPE_IAX = 5
    }

    #endregion

    #region Structs

    internal struct iaxc_sound
    {

        public IntPtr data;
        public int len;
        public int malloced;
        public int channel;
        public int repeat;
        public int pos;
        public int id;
        public IntPtr next;
    }

    internal struct iaxc_ev_text
    {
        public TextType type;
        public int callNo;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
        public string message;
    }

    internal struct iaxc_audio_device
    {
        public string name;
        public AudioCapability capabilities;
        public int devID;
    }

    internal struct iaxc_ev_levels
    {
        public float input;
        public float output;
    }

    internal struct iaxc_ev_registration
    {
        public int id;
        public RegistrationEvent reply;
        public int msgcount;
    }

    internal struct iaxc_ev_call_state
    {
        public int callNo;
        public CallState state;
        public AudioFormat format;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
        public string remote;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
        public string remote_name;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
        public string local;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
        public string local_context;
    }

    /*
    [StructLayout(LayoutKind.Explicit)]
    internal struct iaxc_event
    {

        [FieldOffset(0)]
        public IntPtr next;

        [FieldOffset(4)]
        public EventType type;

        [FieldOffset(8)]
        public iaxc_ev_levels level;

        [FieldOffset(8)]
        public iaxc_ev_text text;

        [FieldOffset(8)]
        public iaxc_ev_call_state callState;

        [FieldOffset(8)]
        public iaxc_ev_registration registration;

    }
    */

    internal struct iaxc_event
    {
        public IntPtr next;
        public EventType type;
    }

    #endregion

    #region Classes

    internal static class IAXFunctions
    {

        #region Consts

        private const string DllImportName = "iaxclient.dll";

        #endregion

        #region Methods

        [DllImport(DllImportName, CallingConvention = CallingConvention.StdCall)]
        public static extern int iaxc_initialize(
            int audType,
            int calls);

        [DllImport(DllImportName, CallingConvention = CallingConvention.StdCall)]
        public static extern void iaxc_shutdown();

        [DllImport(DllImportName, CallingConvention = CallingConvention.StdCall)]
        public static extern void iaxc_set_formats(
            int preferred,
            int allowed);

        [DllImport(DllImportName, CallingConvention = CallingConvention.StdCall)]
        public static extern void iaxc_set_silence_threshold(
            double thr);

        [DllImport(DllImportName, CallingConvention = CallingConvention.StdCall)]
        public static extern int iaxc_audio_devices_get(
            ref IntPtr devs,
            ref int nDevs,
            ref int input,
            ref int output,
            ref int ring);

        [DllImport(DllImportName)]
        public static extern int iaxc_audio_devices_set(
            int input,
            int output,
            int ring);

        [DllImport(DllImportName)]
        public static extern int iaxc_set_event_callpost(
            IntPtr handle,
            int id);

        [DllImport(DllImportName)]
        public static extern int iaxc_start_processing_thread();

        [DllImport(DllImportName)]
        public static extern int iaxc_stop_processing_thread();

        [DllImport(DllImportName)]
        public static extern int iaxc_register(
            [MarshalAs(UnmanagedType.LPStr)] string user,
            [MarshalAs(UnmanagedType.LPStr)] string pass,
            [MarshalAs(UnmanagedType.LPStr)] string host);

        [DllImport(DllImportName)]
        public static extern int iaxc_unregister(
            int id);

        [DllImport(DllImportName)]
        public static extern void iaxc_call(
            [MarshalAs(UnmanagedType.LPStr)] string number);

        [DllImport(DllImportName)]
        public static extern int iaxc_select_call(
            int callNo);

        [DllImport(DllImportName)]
        public static extern void iaxc_free_event(
            IntPtr e);

        [DllImport(DllImportName)]
        public static extern void iaxc_dump_call();

        [DllImport(DllImportName)]
        public static extern void iaxc_answer_call(int callNo);

        [DllImport(DllImportName)]
        public static extern void iaxc_set_callerid(
            [MarshalAs(UnmanagedType.LPStr)] string name,
            [MarshalAs(UnmanagedType.LPStr)] string number);

        [DllImport(DllImportName)]
        public static extern void iaxc_dump_all_calls();

        [DllImport(DllImportName)]
        public static extern void iaxc_send_dtmf(byte digit);

        [DllImport(DllImportName)]
        public static extern IntPtr iaxc_version(IntPtr version);

        [DllImport(DllImportName)]
        public static extern void iaxc_blind_transfer_call(int callNo,
            [MarshalAs(UnmanagedType.LPStr)] string number);

        [DllImport(DllImportName)]
        public static extern int iaxc_play_sound(ref iaxc_sound sound, int ring);

        [DllImport(DllImportName)]
        public static extern int iaxc_stop_sound(int id);

        #endregion

    }

    #endregion
}
